const fs = require('fs');
const path = require('path');
const sharp = require('sharp');
const { promisify } = require('util');

// 使用Node.js内置的fs.promises
const fsPromises = fs.promises;

// 转换为Promise API
const readdir = promisify(fs.readdir);
const stat = promisify(fs.stat);
const mkdirSync = fs.mkdirSync;
const copyFileSync = fs.copyFileSync;
const unlinkSync = fs.unlinkSync;
const statSync = fs.statSync;
const existsSync = fs.existsSync;
const readdirSync = fs.readdirSync;
const rmdirSync = fs.rmdirSync;

// 支持的图片格式
const SUPPORTED_FORMATS = ['.png', '.jpg', '.jpeg', '.webp', '.gif', '.tiff'];

/**
 * 检查文件是否为支持的图片类型
 * @param {string} filePath - 文件路径
 * @returns {boolean} 是否为支持的图片
 */
function isSupportedImage(filePath) {
    const ext = path.extname(filePath).toLowerCase();
    return SUPPORTED_FORMATS.includes(ext);
}

/**
 * 同步删除文件夹（递归）
 * @param {string} dirPath - 文件夹路径
 */
function deleteFolderSync(dirPath) {
    if (existsSync(dirPath)) {
        readdirSync(dirPath).forEach((file) => {
            const curPath = path.join(dirPath, file);
            if (statSync(curPath).isDirectory()) {
                // 递归删除子文件夹
                deleteFolderSync(curPath);
            } else {
                // 删除文件
                unlinkSync(curPath);
            }
        });
        rmdirSync(dirPath);
    }
}

/**
 * 根据图片格式设置最佳压缩参数并进行压缩
 * 压缩后若文件变大或节省0%，则不保留输出文件
 * @param {string} inputPath - 输入图片路径
 * @param {string} outputPath - 输出图片路径
 * @returns {number} 节省的百分比，如果为0或负数表示不需要继续压缩
 */
async function compressImage(inputPath, outputPath) {
    try {
        const ext = path.extname(inputPath).toLowerCase();
        const image = sharp(inputPath);
        const uniQuality = 80
        // 对于不同格式使用最佳压缩参数
        switch (ext) {
            case '.png':
                await image.png({
                    quality: uniQuality,
                    compressionLevel: 6,
                    force: false
                }).toFile(outputPath);
                break;

            case '.jpg':
            case '.jpeg':
                await image.jpeg({
                    quality: uniQuality,
                    mozjpeg: true,
                    force: false
                }).toFile(outputPath);
                break;

            case '.webp':
                await image.webp({
                    quality: uniQuality,
                    force: false
                }).toFile(outputPath);
                break;

            case '.gif':
                await image.gif({
                    quality: uniQuality,
                    force: false
                }).toFile(outputPath);
                break;

            case '.tiff':
                await image.tiff({
                    quality: uniQuality,
                    force: false
                }).toFile(outputPath);
                break;

            default:
                throw new Error(`不支持的图片格式: ${ext}`);
        }

        // 计算压缩前后的文件大小
        const originalSize = (await stat(inputPath)).size;
        const compressedSize = (await stat(outputPath)).size;
        const savings = ((originalSize - compressedSize) / originalSize * 100).toFixed(2);

        // 关键修改：节省率≤0%时，删除输出文件（不保留）
        if (parseFloat(savings) <= 0) {
            await fsPromises.unlink(outputPath);
            if (parseFloat(savings) < 0) {
                console.log(`压缩后文件变大，已删除: ${path.basename(inputPath)} (变大 ${Math.abs(savings)}%)`);
            } else {
                console.log(`压缩无节省空间，已删除: ${path.basename(inputPath)} (节省 0%)`);
            }
            return parseFloat(savings);
        } else {
            console.log(`压缩成功: ${path.basename(inputPath)} (节省 ${savings}%)`);
            return parseFloat(savings);
        }
    } catch (error) {
        console.error(`压缩失败: ${inputPath} - ${error.message}`);
    }
}

/**
 * 递归遍历目录并处理所有图片（使用临时目录进行循环压缩）
 * @param {string} inputDir - 输入目录
 * @param {string} outputDir - 输出目录
 * @param {string} tempDir - 临时目录
 */
async function processDirectoryWithTemp(inputDir, outputDir, tempDir) {
    try {
        // 确保输出目录和临时目录存在
        mkdirSync(outputDir, { recursive: true });
        mkdirSync(tempDir, { recursive: true });

        const entries = readdirSync(inputDir, { withFileTypes: true });

        for (const entry of entries) {
            const inputPath = path.join(inputDir, entry.name);
            const outputPath = path.join(outputDir, entry.name);
            
            // 正确处理临时文件扩展名
            const fileName = path.basename(entry.name, path.extname(entry.name));
            const fileExt = path.extname(entry.name);
            const tempPath1 = path.join(tempDir, `${fileName}_temp1${fileExt}`);
            const tempPath2 = path.join(tempDir, `${fileName}_temp2${fileExt}`);

            if (entry.isDirectory()) {
                const subTempDir = path.join(tempDir, entry.name);
                const subOutputDir = path.join(outputDir, entry.name);
                await processDirectoryWithTemp(inputPath, subOutputDir, subTempDir);
            } else if (entry.isFile() && isSupportedImage(inputPath)) {
                console.log(`开始处理图片: ${path.basename(inputPath)}`);

                // 初始化：复制原始文件到第一个临时文件
                copyFileSync(inputPath, tempPath1);

                let hasImprovement = true;
                let round = 1;

                // 循环压缩直到无法进一步压缩
                while (hasImprovement) {
                    console.log(`  第${round}轮压缩...`);

                    // 压缩临时文件1到临时文件2
                    const savings = await compressImage(tempPath1, tempPath2);

                    if (savings > 0) {
                        // 如果有改进，准备下一轮压缩
                        if (existsSync(tempPath1)) {
                            unlinkSync(tempPath1);
                        }
                        copyFileSync(tempPath2, tempPath1);
                        if (existsSync(tempPath2)) {
                            unlinkSync(tempPath2);
                        }
                        round++;
                    } else {
                        // 如果没有改进，结束循环
                        hasImprovement = false;

                        // 确保临时文件1存在
                        if (!existsSync(tempPath1)) {
                            copyFileSync(inputPath, tempPath1);
                        }

                        // 将最终结果复制到输出目录
                        copyFileSync(tempPath1, outputPath);
                        console.log(`  图片处理完成: ${path.basename(inputPath)} (最终大小: ${(statSync(outputPath).size / 1024).toFixed(2)}KB)`);
                    }
                }

                // 清理临时文件
                if (existsSync(tempPath1)) {
                    unlinkSync(tempPath1);
                }
                if (existsSync(tempPath2)) {
                    unlinkSync(tempPath2);
                }
            }
        }
    } catch (error) {
        console.error(`处理目录出错: ${inputDir} - ${error.message}`);
    }
}

/**
 * 递归遍历目录并处理所有图片
 * @param {string} inputDir - 输入目录
 * @param {string} outputDir - 输出目录
 */
async function processDirectory(inputDir, outputDir) {
    // 创建临时目录
    const tempDir = path.join(__dirname, 'temp_compression');

    try {
        // 使用临时目录进行循环压缩
        await processDirectoryWithTemp(inputDir, outputDir, tempDir);
    } finally {
        // 清理临时目录
        try {
            if (existsSync(tempDir)) {
                deleteFolderSync(tempDir);
                console.log('临时目录已清理');
            }
        } catch (err) {
            console.error('清理临时目录时出错:', err.message);
        }
    }
}

/**
 * 主函数
 * @param {string} inputDir - 输入目录路径
 * @param {string} outputDir - 输出目录路径
 */
async function main(inputDir, outputDir) {
    if (!inputDir || !outputDir) {
        console.error('请提供输入目录和输出目录');
        console.log('用法: node image-compressor.js <输入目录> <输出目录>');
        process.exit(1);
    }

    const resolvedInput = path.resolve(inputDir);
    const resolvedOutput = path.resolve(outputDir);

    try {
        await stat(resolvedInput);
    } catch (error) {
        console.error(`输入目录不存在: ${resolvedInput}`);
        process.exit(1);
    }

    console.log(`开始图片压缩...`);
    console.log(`输入目录: ${resolvedInput}`);
    console.log(`输出目录: ${resolvedOutput}`);
    console.log('------------------------------');

    try {
        console.log('开始循环压缩图片...');
        console.log('将持续压缩直到图片无法进一步减小或开始变大');
        console.log('------------------------------');

        await processDirectory(resolvedInput, resolvedOutput);

        console.log('------------------------------');
        console.log('所有图片处理完成!');
        console.log('最终压缩结果已保存到:', resolvedOutput);
    } catch (error) {
        console.error('处理过程中发生错误:', error.message);

        // 确保清理临时目录
        const tempDir = path.join(__dirname, 'temp_compression');
        try {
            if (existsSync(tempDir)) {
                deleteFolderSync(tempDir);
            }
        } catch (err) {
            console.error('清理临时目录时出错:', err.message);
        }

        process.exit(1);
    }
}

// 从命令行获取参数
const [, , inputDir, outputDir] = process.argv;
main(inputDir, outputDir);